# CMake build file list for OpenAL

cmake_minimum_required(VERSION 3.13)
enable_testing()

if(APPLE)
    # The workaround for try_compile failing with code signing
    # since cmake-3.18.2, not required
    set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
        ${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES}
        "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED"
        "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED")
    set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO)
    set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
    # Fix compile failure with armv7 deployment target >= 11.0, xcode clang
    # will report:
    # error: invalid iOS deployment version '--target=armv7-apple-ios13.6',
    # iOS 10 is the maximum deployment target for 32-bit targets
    # If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest
    # deployment target
    if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*")
        if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET
            OR NOT CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0")
            message(STATUS "Forcing iOS deployment target to 10.0 for armv7")
            set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0" CACHE STRING "Minimum OS X deployment version"
                FORCE)
        endif()
    endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
    set(ALSOFT_UWP TRUE)
endif()

if(COMMAND CMAKE_POLICY)
    cmake_policy(SET CMP0003 NEW)
    cmake_policy(SET CMP0005 NEW)
    if(POLICY CMP0020)
        cmake_policy(SET CMP0020 NEW)
    endif(POLICY CMP0020)
    if(POLICY CMP0042)
        cmake_policy(SET CMP0042 NEW)
    endif(POLICY CMP0042)
    if(POLICY CMP0054)
        cmake_policy(SET CMP0054 NEW)
    endif(POLICY CMP0054)
    if(POLICY CMP0058)
        cmake_policy(SET CMP0058 NEW)
    endif(POLICY CMP0058)
    if(POLICY CMP0063)
        cmake_policy(SET CMP0063 NEW)
    endif(POLICY CMP0063)
    if(POLICY CMP0075)
        cmake_policy(SET CMP0075 NEW)
    endif(POLICY CMP0075)
    if(POLICY CMP0092)
        cmake_policy(SET CMP0092 NEW)
    endif(POLICY CMP0092)
    if(POLICY CMP0117)
        cmake_policy(SET CMP0117 NEW)
    endif(POLICY CMP0117)
endif(COMMAND CMAKE_POLICY)

project(OpenAL)

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
        "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
        FORCE)
endif()
if(NOT CMAKE_DEBUG_POSTFIX)
    set(CMAKE_DEBUG_POSTFIX "" CACHE STRING
        "Library postfix for debug builds. Normally left blank."
        FORCE)
endif()

set(ALSOFT_STD_VERSION_PROPS
    # Require C++17.
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED TRUE
    # Prefer C17, but support earlier when necessary.
    C_STANDARD 17)

list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

include(CheckFunctionExists)
include(CheckLibraryExists)
include(CheckIncludeFile)
include(CheckIncludeFiles)
include(CheckSymbolExists)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
include(CheckLinkerFlag)
include(CheckStructHasMember)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)

find_package(PkgConfig)
find_package(SDL3 QUIET)

option(ALSOFT_DLOPEN  "Check for the dlopen API for loading optional libs"  ON)

option(ALSOFT_WERROR  "Treat compile warnings as errors"      OFF)

option(ALSOFT_UTILS "Build utility programs"  ON)
option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)

option(ALSOFT_EXAMPLES  "Build example programs"  ON)
option(ALSOFT_TESTS "Build test programs"  OFF)

option(ALSOFT_INSTALL "Install main library" ON)
option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON)
option(ALSOFT_INSTALL_HRTF_DATA "Install HRTF data files" ON)
option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" ON)
option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON)
option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON)
option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)

option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})

option(ALSOFT_SEARCH_INSTALL_DATADIR "Search the installation data directory" OFF)
if(ALSOFT_SEARCH_INSTALL_DATADIR)
    set(ALSOFT_INSTALL_DATADIR ${CMAKE_INSTALL_FULL_DATADIR})
endif()

if(DEFINED SHARE_INSTALL_DIR)
    message(WARNING "SHARE_INSTALL_DIR is deprecated.  Use the variables provided by the GNUInstallDirs module instead")
    set(CMAKE_INSTALL_DATADIR "${SHARE_INSTALL_DIR}")
endif()

if(DEFINED LIB_SUFFIX)
    message(WARNING "LIB_SUFFIX is deprecated.  Use the variables provided by the GNUInstallDirs module instead")
endif()
if(DEFINED ALSOFT_CONFIG)
    message(WARNING "ALSOFT_CONFIG is deprecated. Use ALSOFT_INSTALL_CONFIG instead")
endif()
if(DEFINED ALSOFT_HRTF_DEFS)
    message(WARNING "ALSOFT_HRTF_DEFS is deprecated. Use ALSOFT_INSTALL_HRTF_DATA instead")
endif()
if(DEFINED ALSOFT_AMBDEC_PRESETS)
    message(WARNING "ALSOFT_AMBDEC_PRESETS is deprecated. Use ALSOFT_INSTALL_AMBDEC_PRESETS instead")
endif()

if(MSVC)
    option(FORCE_STATIC_VCRT "Force /MT for static VC runtimes" OFF)
    if(FORCE_STATIC_VCRT)
        foreach(flag_var
                CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
                CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
                CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
                CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
            if(${flag_var} MATCHES "/MD")
                string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
            endif()
        endforeach(flag_var)
    endif()
endif()

add_subdirectory(fmt-11.1.1 EXCLUDE_FROM_ALL)


set(CPP_DEFS ) # C pre-processor, not C++
set(INC_PATHS )
set(C_FLAGS )
set(LINKER_FLAGS )
set(LINKER_FLAGS_DEBUG )
set(LINKER_FLAGS_RELEASE )
set(EXTRA_LIBS )

if(WIN32)
    set(CPP_DEFS ${CPP_DEFS} _WIN32 NOMINMAX)
    if(MINGW)
        set(CPP_DEFS ${CPP_DEFS} __USE_MINGW_ANSI_STDIO)
    endif()

    option(ALSOFT_BUILD_ROUTER  "Build the router (EXPERIMENTAL; creates OpenAL32.dll and soft_oal.dll)"  OFF)
    if(MINGW)
        option(ALSOFT_BUILD_IMPORT_LIB "Build an import .lib using dlltool (requires sed)" ON)
    endif()

    if(NOT ALSOFT_UWP)
        # Some systems may need NTDDI_VERSION defined to NTDDI_VISTA or later
        check_c_source_compiles("#define INITGUID
            #include <shlobj.h>
            #include <knownfolders.h>
            int main()
            {
                SHGetKnownFolderPath(&FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND, NULL, NULL);
                return 0;
            }" HAVE_SHGETKNOWNFOLDERPATH_NO_NTDDI)
        if(NOT HAVE_SHGETKNOWNFOLDERPATH_NO_NTDDI)
            set(CPP_DEFS ${CPP_DEFS} NTDDI_VERSION=NTDDI_VISTA)
        endif()
    endif()
elseif(APPLE)
    option(ALSOFT_OSX_FRAMEWORK "Build as macOS framework" OFF)
endif()


# QNX's gcc do not uses /usr/include and /usr/lib paths by default
if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
    set(INC_PATHS ${INC_PATHS} /usr/include)
    set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib)
endif()

# When the library is built for static linking, apps should define
# AL_LIBTYPE_STATIC when including the AL headers.
if(NOT LIBTYPE)
    set(LIBTYPE SHARED)
endif()

set(LIB_MAJOR_VERSION "1")
set(LIB_MINOR_VERSION "24")
set(LIB_REVISION "3")
set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)

set(EXPORT_DECL "")


# Some systems erroneously require the __STDC_FORMAT_MACROS macro to be defined
# to get the fixed-width integer type formatter macros.
check_cxx_source_compiles("#include <cinttypes>
#include <cstdio>
int main()
{
    int64_t i64{};
    std::printf(\"%\" PRId64, i64);
}"
HAVE_STDC_FORMAT_MACROS)
if(NOT HAVE_STDC_FORMAT_MACROS)
    set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
endif()

# Some systems may need libatomic for atomic functions to work
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic)
check_cxx_source_compiles("#include <atomic>
std::atomic<int> foo{0};
int main() { return foo.fetch_add(2); }"
HAVE_LIBATOMIC)
if(NOT HAVE_LIBATOMIC)
    set(CMAKE_REQUIRED_LIBRARIES "${OLD_REQUIRED_LIBRARIES}")
else()
    set(EXTRA_LIBS atomic ${EXTRA_LIBS})
endif()
unset(OLD_REQUIRED_LIBRARIES)

if(ANDROID)
    # Include liblog for Android logging
    check_library_exists(log __android_log_print "" HAVE_LIBLOG)
    if(HAVE_LIBLOG)
        set(EXTRA_LIBS log ${EXTRA_LIBS})
        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log)
    endif()
endif()

if(MSVC)
    # NOTE: _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR is temporary. When building on
    # VS 2022 17.10 or newer, but using an older runtime, mutexes can crash
    # when locked. Ideally the runtime should be updated on the system, but
    # until the update becomes more widespread, this helps avoid some pain
    # points.
    set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
    check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH)
    if(HAVE_PERMISSIVE_SWITCH)
        set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:/permissive->)
    endif()
    set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051
        $<$<COMPILE_LANGUAGE:CXX>:/EHsc> /utf-8)

    if(NOT DXSDK_DIR)
        string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}")
    else()
        string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "${DXSDK_DIR}")
    endif()
    if(DXSDK_DIR)
        message(STATUS "Using DirectX SDK directory: ${DXSDK_DIR}")
    endif()
else()
    set(C_FLAGS ${C_FLAGS} -Winline -Wunused -Wall -Wextra -Wshadow -Wconversion -Wcast-align
        -Wpedantic
        $<$<COMPILE_LANGUAGE:CXX>:-Wold-style-cast -Wnon-virtual-dtor -Woverloaded-virtual>)

    check_cxx_compiler_flag(-Wno-c++20-attribute-extensions HAVE_WNO_CXX20_ATTR_EXT)
    if(HAVE_WNO_CXX20_ATTR_EXT)
        set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-c++20-attribute-extensions>)
    else()
        check_cxx_compiler_flag(-Wno-c++20-extensions HAVE_WNO_CXX20_EXT)
        if(HAVE_WNO_CXX20_EXT)
            set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-c++20-extensions>)
        endif()
    endif()

    check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE)
    if(HAVE_WNO_INTERFERENCE_SIZE)
        set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-interference-size>)
    endif()

    if(ALSOFT_WERROR)
        set(C_FLAGS ${C_FLAGS} -Werror)
    else()
        set(C_FLAGS ${C_FLAGS} -Werror=undef)
    endif()

    # NOTE: This essentially provides the equivalent of the C++26 feature to
    # initialize all local variables with a non-0 bit pattern. Until C++26 is
    # adopted, the [[gnu::uninitialized]] attribute will avoid the auto-
    # initialization where necessary.
    check_c_compiler_flag(-ftrivial-auto-var-init=pattern HAVE_FTRIVIAL_AUTO_VAR_INIT)
    if(HAVE_FTRIVIAL_AUTO_VAR_INIT)
        set(C_FLAGS ${C_FLAGS} -ftrivial-auto-var-init=pattern)
    endif()

    check_c_compiler_flag(-fno-math-errno HAVE_FNO_MATH_ERRNO)
    if(HAVE_FNO_MATH_ERRNO)
        set(C_FLAGS ${C_FLAGS} -fno-math-errno)
    endif()

    option(ALSOFT_STATIC_LIBGCC "Force -static-libgcc for static GCC runtimes" OFF)
    if(ALSOFT_STATIC_LIBGCC)
        set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} -static-libgcc)
        check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBGCC_SWITCH)
        set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
        unset(OLD_REQUIRED_LIBRARIES)

        if(NOT HAVE_STATIC_LIBGCC_SWITCH)
            message(FATAL_ERROR "Cannot static link libgcc")
        endif()
        set(LINKER_FLAGS ${LINKER_FLAGS} -static-libgcc)
    endif()

    option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF)
    if(ALSOFT_STATIC_STDCXX)
        set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++")
        check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH)
        set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
        unset(OLD_REQUIRED_LIBRARIES)

        if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH)
            message(FATAL_ERROR "Cannot static link libstdc++")
        endif()
        set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++")
    endif()

    if(WIN32)
        option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF)
        if(ALSOFT_STATIC_WINPTHREAD)
            set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
            set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
            check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH)
            set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
            unset(OLD_REQUIRED_LIBRARIES)

            if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH)
                message(FATAL_ERROR "Cannot static link libwinpthread")
            endif()
            set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
        endif()
    endif()
endif()

# Set visibility/export options if available
if(NOT LIBTYPE STREQUAL "STATIC")
    if(WIN32)
        set(EXPORT_DECL "__declspec(dllexport)")
    else()
        set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
        # Yes GCC, really don't accept visibility modes you don't support
        set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")

        check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
                                int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
        if(HAVE_GCC_PROTECTED_VISIBILITY)
            set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
        else()
            check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
                                    int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
            if(HAVE_GCC_DEFAULT_VISIBILITY)
                set(EXPORT_DECL "__attribute__((visibility(\"default\")))")
            endif()
        endif()
        if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
            set(CMAKE_C_VISIBILITY_PRESET hidden)
            set(CMAKE_CXX_VISIBILITY_PRESET hidden)
        endif()

        set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
    endif()
endif()


set(SSE2_SWITCH "")

if(NOT MSVC)
    set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
    # Yes GCC, really don't accept command line options you don't support
    set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Werror")
    check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH)
    if(HAVE_MSSE2_SWITCH)
        set(SSE2_SWITCH "-msse2")
    endif()
    set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
    unset(OLD_REQUIRED_FLAGS)
endif()

check_include_file(xmmintrin.h HAVE_XMMINTRIN_H)
check_include_file(emmintrin.h HAVE_EMMINTRIN_H)
check_include_file(pmmintrin.h HAVE_PMMINTRIN_H)
check_include_file(smmintrin.h HAVE_SMMINTRIN_H)
check_include_file(arm_neon.h HAVE_ARM_NEON_H)

set(HAVE_SSE        0)
set(HAVE_SSE2       0)
set(HAVE_SSE3       0)
set(HAVE_SSE4_1     0)
set(HAVE_NEON       0)

# Check for SSE support
option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON)
option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF)
if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H)
    set(HAVE_SSE 1)
endif()
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
    message(FATAL_ERROR "Failed to enable required SSE CPU extensions")
endif()

option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF)
if(ALSOFT_CPUEXT_SSE2 AND HAVE_SSE AND HAVE_EMMINTRIN_H)
    set(HAVE_SSE2 1)
endif()
if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
    message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
endif()

option(ALSOFT_CPUEXT_SSE3 "Enable SSE3 support" ON)
option(ALSOFT_REQUIRE_SSE3 "Require SSE3 support" OFF)
if(ALSOFT_CPUEXT_SSE3 AND HAVE_SSE2 AND HAVE_PMMINTRIN_H)
    set(HAVE_SSE3 1)
endif()
if(ALSOFT_REQUIRE_SSE3 AND NOT HAVE_SSE3)
    message(FATAL_ERROR "Failed to enable required SSE3 CPU extensions")
endif()

option(ALSOFT_CPUEXT_SSE4_1 "Enable SSE4.1 support" ON)
option(ALSOFT_REQUIRE_SSE4_1 "Require SSE4.1 support" OFF)
if(ALSOFT_CPUEXT_SSE4_1 AND HAVE_SSE3 AND HAVE_SMMINTRIN_H)
    set(HAVE_SSE4_1 1)
endif()
if(ALSOFT_REQUIRE_SSE4_1 AND NOT HAVE_SSE4_1)
    message(FATAL_ERROR "Failed to enable required SSE4.1 CPU extensions")
endif()

# Check for ARM Neon support
option(ALSOFT_CPUEXT_NEON "Enable ARM NEON support" ON)
option(ALSOFT_REQUIRE_NEON "Require ARM NEON support" OFF)
if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H)
    check_c_source_compiles("#include <arm_neon.h>
        int main()
        {
            int32x4_t ret4 = vdupq_n_s32(0);
            return vgetq_lane_s32(ret4, 0);
        }" HAVE_NEON_INTRINSICS)
    if(HAVE_NEON_INTRINSICS)
        set(HAVE_NEON 1)
    endif()
endif()
if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
    message(FATAL_ERROR "Failed to enable required ARM NEON CPU extensions")
endif()


set(ALSOFT_FORCE_ALIGN )
set(SSE_FLAGS )
set(FPMATH_SET "0")
if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
    option(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE)
    if(ALSOFT_ENABLE_SSE2_CODEGEN)
        if(MSVC)
            check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2)
            if(HAVE_ARCH_SSE2)
                set(SSE_FLAGS ${SSE_FLAGS} "/arch:SSE2")
                set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS})
                set(FPMATH_SET 2)
            endif()
        elseif(SSE2_SWITCH)
            check_c_compiler_flag("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2)
            if(HAVE_MFPMATH_SSE_2)
                set(SSE_FLAGS ${SSE_FLAGS} ${SSE2_SWITCH} -mfpmath=sse)
                set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS})
                set(FPMATH_SET 2)
            endif()
            # SSE depends on a 16-byte aligned stack, and by default, GCC
            # assumes the stack is suitably aligned. Older Linux code or other
            # OSs don't guarantee this on 32-bit, so externally-callable
            # functions need to ensure an aligned stack.
            set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))")
            set(ALSOFT_FORCE_ALIGN "__attribute__((force_align_arg_pointer))")
        endif()
    endif()
endif()

if(HAVE_SSE2)
    set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
    foreach(flag_var ${SSE_FLAGS})
        set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag_var}")
    endforeach()

    check_c_source_compiles("#include <emmintrin.h>
        int main() {_mm_pause(); return 0;}" HAVE_SSE_INTRINSICS)

    set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
endif()


check_include_file(cpuid.h HAVE_CPUID_H)
check_include_file(intrin.h HAVE_INTRIN_H)
check_include_file(guiddef.h HAVE_GUIDDEF_H)

# Some systems need libm for some math functions to work
set(MATH_LIB )
check_library_exists(m pow "" HAVE_LIBM)
if(HAVE_LIBM)
    set(MATH_LIB ${MATH_LIB} m)
    set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} m)
endif()

# Some systems need to link with -lrt for clock_gettime as used by the common
# eaxmple functions.
set(RT_LIB )
check_library_exists(rt clock_gettime "" HAVE_LIBRT)
if(HAVE_LIBRT)
    set(RT_LIB rt)
endif()

# Check for the dlopen API (for dynamically loading backend libs)
if(ALSOFT_DLOPEN)
    check_include_file(dlfcn.h HAVE_DLFCN_H)
    check_library_exists(dl dlopen "" HAVE_LIBDL)
    if(HAVE_LIBDL)
        set(EXTRA_LIBS dl ${EXTRA_LIBS})
        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dl)
    endif()
endif()

# Check for a cpuid intrinsic
if(HAVE_CPUID_H)
    check_c_source_compiles("#include <cpuid.h>
        int main()
        {
            unsigned int eax, ebx, ecx, edx;
            return __get_cpuid(0, &eax, &ebx, &ecx, &edx);
        }" HAVE_GCC_GET_CPUID)
endif()
if(HAVE_INTRIN_H)
    check_c_source_compiles("#include <intrin.h>
        int main()
        {
            int regs[4];
            __cpuid(regs, 0);
            return regs[0];
        }" HAVE_CPUID_INTRINSIC)
endif()

check_symbol_exists(proc_pidpath     libproc.h HAVE_PROC_PIDPATH)

if(NOT WIN32)
    # We need pthreads outside of Windows, for semaphores. It's also used to
    # set the priority and name of threads, when possible.
    check_include_file(pthread.h HAVE_PTHREAD_H)
    if(NOT HAVE_PTHREAD_H)
        message(FATAL_ERROR "PThreads is required for non-Windows builds!")
    endif()

    check_c_compiler_flag(-pthread HAVE_PTHREAD)
    if(HAVE_PTHREAD)
        set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -pthread")
        set(C_FLAGS ${C_FLAGS} -pthread)
        set(LINKER_FLAGS ${LINKER_FLAGS} -pthread)
    endif()

    check_symbol_exists(pthread_setschedparam pthread.h HAVE_PTHREAD_SETSCHEDPARAM)

    # Some systems need pthread_np.h to get pthread_setname_np
    check_include_files("pthread.h;pthread_np.h" HAVE_PTHREAD_NP_H)
    if(HAVE_PTHREAD_NP_H)
        check_symbol_exists(pthread_setname_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SETNAME_NP)
        if(NOT HAVE_PTHREAD_SETNAME_NP)
            check_symbol_exists(pthread_set_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SET_NAME_NP)
        endif()
    else()
        check_symbol_exists(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP)
        if(NOT HAVE_PTHREAD_SETNAME_NP)
            check_symbol_exists(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP)
        endif()
    endif()
endif()


# Common sources used by both the OpenAL implementation library, the OpenAL
# router, and certain tools and examples.
set(COMMON_OBJS
    common/alassert.cpp
    common/alassert.h
    common/albit.h
    common/alcomplex.cpp
    common/alcomplex.h
    common/almalloc.h
    common/alnumbers.h
    common/alnumeric.h
    common/alsem.cpp
    common/alsem.h
    common/alspan.h
    common/alstring.cpp
    common/alstring.h
    common/althrd_setname.cpp
    common/althrd_setname.h
    common/althreads.h
    common/altraits.h
    common/atomic.h
    common/comptr.h
    common/dynload.cpp
    common/dynload.h
    common/filesystem.cpp
    common/filesystem.h
    common/flexarray.h
    common/intrusive_ptr.h
    common/opthelpers.h
    common/pffft.cpp
    common/pffft.h
    common/phase_shifter.h
    common/polyphase_resampler.cpp
    common/polyphase_resampler.h
    common/pragmadefs.h
    common/ringbuffer.cpp
    common/ringbuffer.h
    common/strutils.cpp
    common/strutils.h
    common/vecmat.h
    common/vector.h)

# Core library routines
set(CORE_OBJS
    core/ambdec.cpp
    core/ambdec.h
    core/ambidefs.cpp
    core/ambidefs.h
    core/async_event.h
    core/bformatdec.cpp
    core/bformatdec.h
    core/bs2b.cpp
    core/bs2b.h
    core/bsinc_defs.h
    core/bsinc_tables.cpp
    core/bsinc_tables.h
    core/bufferline.h
    core/buffer_storage.cpp
    core/buffer_storage.h
    core/context.cpp
    core/context.h
    core/converter.cpp
    core/converter.h
    core/cpu_caps.cpp
    core/cpu_caps.h
    core/cubic_defs.h
    core/cubic_tables.cpp
    core/cubic_tables.h
    core/devformat.cpp
    core/devformat.h
    core/device.cpp
    core/device.h
    core/effects/base.h
    core/effectslot.cpp
    core/effectslot.h
    core/except.cpp
    core/except.h
    core/filters/biquad.h
    core/filters/biquad.cpp
    core/filters/nfc.cpp
    core/filters/nfc.h
    core/filters/splitter.cpp
    core/filters/splitter.h
    core/fmt_traits.h
    core/fpu_ctrl.cpp
    core/fpu_ctrl.h
    core/front_stablizer.h
    core/helpers.cpp
    core/helpers.h
    core/hrtf.cpp
    core/hrtf.h
    core/logging.cpp
    core/logging.h
    core/mastering.cpp
    core/mastering.h
    core/mixer.cpp
    core/mixer.h
    core/resampler_limits.h
    core/storage_formats.cpp
    core/storage_formats.h
    core/uhjfilter.cpp
    core/uhjfilter.h
    core/uiddefs.cpp
    core/voice.cpp
    core/voice.h
    core/voice_change.h)

set(HAVE_RTKIT 0)
if(NOT WIN32)
    option(ALSOFT_RTKIT "Enable RTKit support" ON)
    option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE)
    if(ALSOFT_RTKIT)
        find_package(DBus1 QUIET)
        if(NOT DBus1_FOUND AND PkgConfig_FOUND)
            pkg_check_modules(DBUS dbus-1)
        endif()
        if(DBus1_FOUND OR DBUS_FOUND)
            set(HAVE_RTKIT 1)
            set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h
                core/rtkit.cpp core/rtkit.h)
            if(NOT DBus1_FOUND)
                set(INC_PATHS ${INC_PATHS} ${DBUS_INCLUDE_DIRS})
                set(CPP_DEFS ${CPP_DEFS} ${DBUS_CFLAGS_OTHER})
                if(NOT HAVE_DLFCN_H)
                    set(EXTRA_LIBS ${EXTRA_LIBS} ${DBUS_LINK_LIBRARIES})
                endif()
            elseif(HAVE_DLFCN_H)
                set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS})
                set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS})
            else()
                set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES})
            endif()
        else()
            set(MISSING_VARS "")
            if(NOT DBus1_INCLUDE_DIRS)
                set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS")
            endif()
            if(NOT DBus1_LIBRARIES)
                set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES")
            endif()
            message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})")
            unset(MISSING_VARS)
        endif()
    endif()
endif()
if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
    message(FATAL_ERROR "Failed to enable required RTKit support")
endif()

# Default mixers, always available
set(CORE_OBJS ${CORE_OBJS}
    core/mixer/defs.h
    core/mixer/hrtfbase.h
    core/mixer/hrtfdefs.h
    core/mixer/mixer_c.cpp)

# AL and related routines
set(OPENAL_OBJS
    al/auxeffectslot.cpp
    al/auxeffectslot.h
    al/buffer.cpp
    al/buffer.h
    al/debug.cpp
    al/debug.h
    al/direct_defs.h
    al/effect.cpp
    al/effect.h
    al/effects/autowah.cpp
    al/effects/chorus.cpp
    al/effects/compressor.cpp
    al/effects/convolution.cpp
    al/effects/dedicated.cpp
    al/effects/distortion.cpp
    al/effects/echo.cpp
    al/effects/effects.cpp
    al/effects/effects.h
    al/effects/equalizer.cpp
    al/effects/fshifter.cpp
    al/effects/modulator.cpp
    al/effects/null.cpp
    al/effects/pshifter.cpp
    al/effects/reverb.cpp
    al/effects/vmorpher.cpp
    al/error.cpp
    al/event.cpp
    al/event.h
    al/extension.cpp
    al/filter.cpp
    al/filter.h
    al/listener.cpp
    al/listener.h
    al/source.cpp
    al/source.h
    al/state.cpp)

# ALC and related routines
set(ALC_OBJS
    alc/alc.cpp
    alc/alu.cpp
    alc/alu.h
    alc/alconfig.cpp
    alc/alconfig.h
    alc/context.cpp
    alc/context.h
    alc/device.cpp
    alc/device.h
    alc/effects/base.h
    alc/effects/autowah.cpp
    alc/effects/chorus.cpp
    alc/effects/compressor.cpp
    alc/effects/convolution.cpp
    alc/effects/dedicated.cpp
    alc/effects/distortion.cpp
    alc/effects/echo.cpp
    alc/effects/equalizer.cpp
    alc/effects/fshifter.cpp
    alc/effects/modulator.cpp
    alc/effects/null.cpp
    alc/effects/pshifter.cpp
    alc/effects/reverb.cpp
    alc/effects/vmorpher.cpp
    alc/events.cpp
    alc/events.h
    alc/export_list.h
    alc/inprogext.h
    alc/panning.cpp)

if(ALSOFT_EAX)
    set(OPENAL_OBJS
        ${OPENAL_OBJS}
        al/eax/api.cpp
        al/eax/api.h
        al/eax/call.cpp
        al/eax/call.h
        al/eax/effect.h
        al/eax/exception.cpp
        al/eax/exception.h
        al/eax/fx_slot_index.cpp
        al/eax/fx_slot_index.h
        al/eax/fx_slots.cpp
        al/eax/fx_slots.h
        al/eax/globals.h
        al/eax/utils.cpp
        al/eax/utils.h
        al/eax/x_ram.h
    )
endif()

# Include SIMD mixers
set(CPU_EXTS "Default")
if(HAVE_SSE)
    set(CORE_OBJS  ${CORE_OBJS} core/mixer/mixer_sse.cpp)
    set(CPU_EXTS "${CPU_EXTS}, SSE")
endif()
if(HAVE_SSE2)
    set(CORE_OBJS  ${CORE_OBJS} core/mixer/mixer_sse2.cpp)
    set(CPU_EXTS "${CPU_EXTS}, SSE2")
endif()
if(HAVE_SSE3)
    set(CORE_OBJS  ${CORE_OBJS} core/mixer/mixer_sse3.cpp)
    set(CPU_EXTS "${CPU_EXTS}, SSE3")
endif()
if(HAVE_SSE4_1)
    set(CORE_OBJS  ${CORE_OBJS} core/mixer/mixer_sse41.cpp)
    set(CPU_EXTS "${CPU_EXTS}, SSE4.1")
endif()
if(HAVE_NEON)
    set(CORE_OBJS  ${CORE_OBJS} core/mixer/mixer_neon.cpp)
    set(CPU_EXTS "${CPU_EXTS}, Neon")
endif()


set(HAVE_ALSA       0)
set(HAVE_OSS        0)
set(HAVE_PIPEWIRE   0)
set(HAVE_SOLARIS    0)
set(HAVE_SNDIO      0)
set(HAVE_DSOUND     0)
set(HAVE_WASAPI     0)
set(HAVE_WINMM      0)
set(HAVE_PORTAUDIO  0)
set(HAVE_PULSEAUDIO 0)
set(HAVE_COREAUDIO  0)
set(HAVE_OPENSL     0)
set(HAVE_OBOE       0)
set(HAVE_OTHERIO    0)
set(HAVE_WAVE       0)
set(HAVE_SDL2       0)
set(HAVE_SDL3       0)

if(WIN32 OR HAVE_DLFCN_H)
    set(IS_LINKED "")
    macro(ADD_BACKEND_LIBS _LIBS)
    endmacro()
else()
    set(IS_LINKED " (linked)")
    macro(ADD_BACKEND_LIBS _LIBS)
        set(EXTRA_LIBS ${_LIBS} ${EXTRA_LIBS})
    endmacro()
endif()

set(BACKENDS "")
set(ALC_OBJS  ${ALC_OBJS}
    alc/backends/base.cpp
    alc/backends/base.h
    # Default backends, always available
    alc/backends/loopback.cpp
    alc/backends/loopback.h
    alc/backends/null.cpp
    alc/backends/null.h
)

# Check PipeWire backend
option(ALSOFT_BACKEND_PIPEWIRE "Enable PipeWire backend" ON)
option(ALSOFT_REQUIRE_PIPEWIRE "Require PipeWire backend" OFF)
if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND)
    pkg_check_modules(PIPEWIRE libpipewire-0.3>=0.3.23)
    if(PIPEWIRE_FOUND)
        set(HAVE_PIPEWIRE 1)
        set(BACKENDS  "${BACKENDS} PipeWire${IS_LINKED},")
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/pipewire.cpp alc/backends/pipewire.h)
        add_backend_libs(${PIPEWIRE_LIBRARIES})
        set(INC_PATHS ${INC_PATHS} ${PIPEWIRE_INCLUDE_DIRS})
    endif()
endif()
if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE)
    message(FATAL_ERROR "Failed to enable required PipeWire backend")
endif()

# Check PulseAudio backend
option(ALSOFT_BACKEND_PULSEAUDIO "Enable PulseAudio backend" ON)
option(ALSOFT_REQUIRE_PULSEAUDIO "Require PulseAudio backend" OFF)
if(ALSOFT_BACKEND_PULSEAUDIO)
    find_package(PulseAudio)
    if(PULSEAUDIO_FOUND)
        set(HAVE_PULSEAUDIO 1)
        set(BACKENDS  "${BACKENDS} PulseAudio${IS_LINKED},")
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/pulseaudio.cpp alc/backends/pulseaudio.h)
        add_backend_libs(${PULSEAUDIO_LIBRARY})
        set(INC_PATHS ${INC_PATHS} ${PULSEAUDIO_INCLUDE_DIR})
    endif()
endif()
if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
    message(FATAL_ERROR "Failed to enable required PulseAudio backend")
endif()

if(NOT WIN32)
    # Check ALSA backend
    option(ALSOFT_BACKEND_ALSA "Enable ALSA backend" ON)
    option(ALSOFT_REQUIRE_ALSA "Require ALSA backend" OFF)
    if(ALSOFT_BACKEND_ALSA)
        find_package(ALSA)
        if(ALSA_FOUND)
            set(HAVE_ALSA 1)
            set(BACKENDS  "${BACKENDS} ALSA${IS_LINKED},")
            set(ALC_OBJS  ${ALC_OBJS} alc/backends/alsa.cpp alc/backends/alsa.h)
            add_backend_libs(${ALSA_LIBRARIES})
            set(INC_PATHS ${INC_PATHS} ${ALSA_INCLUDE_DIRS})
        endif()
    endif()

    # Check OSS backend
    option(ALSOFT_BACKEND_OSS "Enable OSS backend" ON)
    option(ALSOFT_REQUIRE_OSS "Require OSS backend" OFF)
    if(ALSOFT_BACKEND_OSS)
        find_package(OSS)
        if(OSS_FOUND)
            set(HAVE_OSS 1)
            set(BACKENDS  "${BACKENDS} OSS,")
            set(ALC_OBJS  ${ALC_OBJS} alc/backends/oss.cpp alc/backends/oss.h)
            if(OSS_LIBRARIES)
                set(EXTRA_LIBS ${OSS_LIBRARIES} ${EXTRA_LIBS})
            endif()
            set(INC_PATHS ${INC_PATHS} ${OSS_INCLUDE_DIRS})
        endif()
    endif()

    # Check Solaris backend
    option(ALSOFT_BACKEND_SOLARIS "Enable Solaris backend" ON)
    option(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF)
    if(ALSOFT_BACKEND_SOLARIS)
        find_package(AudioIO)
        if(AUDIOIO_FOUND)
            set(HAVE_SOLARIS 1)
            set(BACKENDS  "${BACKENDS} Solaris,")
            set(ALC_OBJS  ${ALC_OBJS} alc/backends/solaris.cpp alc/backends/solaris.h)
            set(INC_PATHS ${INC_PATHS} ${AUDIOIO_INCLUDE_DIRS})
        endif()
    endif()

    # Check SndIO backend (disabled by default on non-BSDs)
    if(BSD)
        option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
    else()
        option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" OFF)
    endif()
    option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
    if(ALSOFT_BACKEND_SNDIO)
        find_package(SndIO)
        if(SNDIO_FOUND)
            set(HAVE_SNDIO 1)
            set(BACKENDS  "${BACKENDS} SndIO (linked),")
            set(ALC_OBJS  ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.hpp)
            set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
            set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
        endif()
    endif()
endif()
if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
    message(FATAL_ERROR "Failed to enable required ALSA backend")
endif()
if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
    message(FATAL_ERROR "Failed to enable required OSS backend")
endif()
if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
    message(FATAL_ERROR "Failed to enable required Solaris backend")
endif()
if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
    message(FATAL_ERROR "Failed to enable required SndIO backend")
endif()

# Check Windows-only backends
if(WIN32)
    if (NOT ALSOFT_UWP)
        # Check MMSystem backend
        option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
        option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
        if(ALSOFT_BACKEND_WINMM)
            set(HAVE_WINMM 1)
            set(BACKENDS "${BACKENDS} WinMM,")
            set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
            # There doesn't seem to be good way to search for winmm.lib for MSVC.
            # find_library doesn't find it without being told to look in a specific
            # place in the WindowsSDK, but it links anyway. If there ends up being
            # Windows targets without this, another means to detect it is needed.
            set(EXTRA_LIBS winmm ${EXTRA_LIBS})
        endif()

        # Check DSound backend
        option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
        option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
        if(ALSOFT_BACKEND_DSOUND)
            check_include_file(dsound.h HAVE_DSOUND_H)
            if(DXSDK_DIR)
                find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
                    PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
                    DOC "The DirectSound include directory")
            endif()
            if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
                set(HAVE_DSOUND 1)
                set(BACKENDS "${BACKENDS} DirectSound,")
                set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)

                if(NOT HAVE_DSOUND_H)
                    set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
                endif()
            endif()
        endif()
    endif()

    # Check for WASAPI backend
    option(ALSOFT_BACKEND_WASAPI "Enable WASAPI backend" ON)
    option(ALSOFT_REQUIRE_WASAPI "Require WASAPI backend" OFF)
    if(ALSOFT_BACKEND_WASAPI)
        check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H)
        if(HAVE_MMDEVICEAPI_H)
            set(HAVE_WASAPI 1)
            set(BACKENDS  "${BACKENDS} WASAPI,")
            set(ALC_OBJS  ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h)

            if(NOT ALSOFT_UWP)
                set(EXTRA_LIBS avrt ${EXTRA_LIBS})
            endif()
        endif()
    endif()

    option(ALSOFT_BACKEND_OTHERIO "Enable OtherIO backend" OFF)
    option(ALSOFT_REQUIRE_OTHERIO "Require OtherIO backend" OFF)
    if(ALSOFT_BACKEND_OTHERIO)
        set(HAVE_OTHERIO 1)
        set(BACKENDS  "${BACKENDS} OtherIO,")
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/otherio.cpp alc/backends/otherio.h)
    endif()
endif()
if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
    message(FATAL_ERROR "Failed to enable required WinMM backend")
endif()
if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND)
    message(FATAL_ERROR "Failed to enable required DSound backend")
endif()
if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
    message(FATAL_ERROR "Failed to enable required WASAPI backend")
endif()
if(ALSOFT_REQUIRE_OTHERIO AND NOT HAVE_OTHERIO)
    message(FATAL_ERROR "Failed to enable required OtherIO backend")
endif()

# Check JACK backend
option(ALSOFT_BACKEND_JACK "Enable JACK backend" ON)
option(ALSOFT_REQUIRE_JACK "Require JACK backend" OFF)
if(ALSOFT_BACKEND_JACK)
    find_package(JACK)
    if(JACK_FOUND)
        set(HAVE_JACK 1)
        set(BACKENDS  "${BACKENDS} JACK${IS_LINKED},")
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/jack.cpp alc/backends/jack.h)
        add_backend_libs(${JACK_LIBRARIES})
        set(INC_PATHS ${INC_PATHS} ${JACK_INCLUDE_DIRS})
    endif()
endif()
if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
    message(FATAL_ERROR "Failed to enable required JACK backend")
endif()

# Check CoreAudio backend
option(ALSOFT_BACKEND_COREAUDIO "Enable CoreAudio backend" ON)
option(ALSOFT_REQUIRE_COREAUDIO "Require CoreAudio backend" OFF)
if(ALSOFT_BACKEND_COREAUDIO)
    find_library(COREAUDIO_FRAMEWORK NAMES CoreAudio)
    find_path(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit/AudioUnit.h)
    if(COREAUDIO_FRAMEWORK AND AUDIOUNIT_INCLUDE_DIR)
        set(HAVE_COREAUDIO 1)
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/coreaudio.cpp alc/backends/coreaudio.h)
        set(BACKENDS  "${BACKENDS} CoreAudio,")

        set(EXTRA_LIBS -Wl,-framework,CoreAudio ${EXTRA_LIBS})
        if(CMAKE_SYSTEM_NAME MATCHES "^(iOS|tvOS)$")
            find_library(COREFOUNDATION_FRAMEWORK NAMES CoreFoundation)
            if(COREFOUNDATION_FRAMEWORK)
                set(EXTRA_LIBS -Wl,-framework,CoreFoundation ${EXTRA_LIBS})
            endif()
        else()
            set(EXTRA_LIBS -Wl,-framework,AudioUnit,-framework,ApplicationServices ${EXTRA_LIBS})
        endif()

        # Some versions of OSX may need the AudioToolbox framework. Add it if
        # it's found.
        find_library(AUDIOTOOLBOX_LIBRARY NAMES AudioToolbox)
        if(AUDIOTOOLBOX_LIBRARY)
            set(EXTRA_LIBS -Wl,-framework,AudioToolbox ${EXTRA_LIBS})
        endif()

        set(INC_PATHS ${INC_PATHS} ${AUDIOUNIT_INCLUDE_DIR})
    endif()
endif()
if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO)
    message(FATAL_ERROR "Failed to enable required CoreAudio backend")
endif()

# Check for Oboe (Android) backend
option(ALSOFT_BACKEND_OBOE "Enable Oboe backend" ON)
option(ALSOFT_REQUIRE_OBOE "Require Oboe backend" OFF)
if(ALSOFT_BACKEND_OBOE)
    set(OBOE_TARGET )
    if(ANDROID)
        set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.")
        if(OBOE_SOURCE)
            add_subdirectory(${OBOE_SOURCE} ./oboe EXCLUDE_FROM_ALL)
            set(OBOE_TARGET oboe)
        else()
            find_package(oboe CONFIG)
            if(NOT TARGET oboe::oboe)
                find_package(Oboe)
            endif()
            if(TARGET oboe::oboe)
                set(OBOE_TARGET "oboe::oboe")
            endif()
        endif()
    endif()

    if(OBOE_TARGET)
        set(HAVE_OBOE 1)
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/oboe.cpp alc/backends/oboe.h)
        set(BACKENDS  "${BACKENDS} Oboe,")
        set(EXTRA_LIBS ${OBOE_TARGET} ${EXTRA_LIBS})
    endif()
endif()
if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
    message(FATAL_ERROR "Failed to enable required Oboe backend")
endif()

# Check for OpenSL (Android) backend
option(ALSOFT_BACKEND_OPENSL "Enable OpenSL backend" ON)
option(ALSOFT_REQUIRE_OPENSL "Require OpenSL backend" OFF)
if(ALSOFT_BACKEND_OPENSL)
    find_package(OpenSL)
    if(OPENSL_FOUND)
        set(HAVE_OPENSL 1)
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h)
        set(BACKENDS  "${BACKENDS} OpenSL${IS_LINKED},")
        add_backend_libs(${OPENSL_LIBRARIES})
        set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
    endif()
endif()
if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
    message(FATAL_ERROR "Failed to enable required OpenSL backend")
endif()

# Check PortAudio backend
option(ALSOFT_BACKEND_PORTAUDIO "Enable PortAudio backend" ON)
option(ALSOFT_REQUIRE_PORTAUDIO "Require PortAudio backend" OFF)
if(ALSOFT_BACKEND_PORTAUDIO)
    find_package(PortAudio)
    if(PORTAUDIO_FOUND)
        set(HAVE_PORTAUDIO 1)
        set(BACKENDS  "${BACKENDS} PortAudio${IS_LINKED},")
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.hpp)
        add_backend_libs(${PORTAUDIO_LIBRARIES})
        set(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS})
    endif()
endif()
if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
    message(FATAL_ERROR "Failed to enable required PortAudio backend")
endif()

# Check for SDL2 or SDL3 backend
# Off by default, since it adds a runtime dependency. Additionally, both SDL2
# and SDL3 can't be enabled simultaneously.
option(ALSOFT_BACKEND_SDL3 "Enable SDL3 backend" OFF)
option(ALSOFT_REQUIRE_SDL3 "Require SDL3 backend" OFF)
if(ALSOFT_BACKEND_SDL3)
    if(SDL3_FOUND)
        set(HAVE_SDL3 1)
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/sdl3.cpp alc/backends/sdl3.h)
        set(BACKENDS  "${BACKENDS} SDL3,")
        set(EXTRA_LIBS ${EXTRA_LIBS} SDL3::SDL3)
    else()
        message(STATUS "Could NOT find SDL3")
    endif()
endif()
if(ALSOFT_REQUIRE_SDL3 AND NOT HAVE_SDL3)
    message(FATAL_ERROR "Failed to enable required SDL3 backend")
endif()

option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF)
option(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF)
if(ALSOFT_BACKEND_SDL2 AND NOT HAVE_SDL3)
    find_package(SDL2)
    if(SDL2_FOUND)
        set(HAVE_SDL2 1)
        set(ALC_OBJS  ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h)
        set(BACKENDS  "${BACKENDS} SDL2,")
        set(EXTRA_LIBS ${EXTRA_LIBS} SDL2::SDL2)
    else()
        message(STATUS "Could NOT find SDL2")
    endif()
endif()
if(ALSOFT_REQUIRE_SDL2 AND NOT HAVE_SDL2)
    message(FATAL_ERROR "Failed to enable required SDL2 backend")
endif()

# Optionally enable the Wave Writer backend
option(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON)
if(ALSOFT_BACKEND_WAVE)
    set(HAVE_WAVE 1)
    set(ALC_OBJS  ${ALC_OBJS} alc/backends/wave.cpp alc/backends/wave.h)
    set(BACKENDS  "${BACKENDS} WaveFile,")
endif()

# This is always available
set(BACKENDS  "${BACKENDS} Null")


find_package(Git)
if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.git")
    set(GIT_DIR "${OpenAL_SOURCE_DIR}/.git")

    # Check if this is a submodule, if it is then find the .git directory
    if(NOT IS_DIRECTORY "${OpenAL_SOURCE_DIR}/.git")
        file(READ ${GIT_DIR} submodule)
        string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule})
        string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
        get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
        get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
    endif()

    # Get the current working branch and its latest abbreviated commit hash
    add_custom_command(OUTPUT "${OpenAL_BINARY_DIR}/version_witness.txt"
        BYPRODUCTS "${OpenAL_BINARY_DIR}/version.h"
        COMMAND ${CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE} -D LIB_VERSION=${LIB_VERSION}
            -D LIB_VERSION_NUM=${LIB_VERSION_NUM} -D SRC=${OpenAL_SOURCE_DIR}/version.h.in
            -D DST=${OpenAL_BINARY_DIR}/version.h -P ${OpenAL_SOURCE_DIR}/version.cmake
        COMMAND ${CMAKE_COMMAND} -E touch "${OpenAL_BINARY_DIR}/version_witness.txt"
        WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}"
        MAIN_DEPENDENCY "${OpenAL_SOURCE_DIR}/version.h.in"
        DEPENDS "${GIT_DIR}/index" "${OpenAL_SOURCE_DIR}/version.cmake"
        VERBATIM
    )

    add_custom_target(alsoft.build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt")
else()
    set(GIT_BRANCH "UNKNOWN")
    set(GIT_COMMIT_HASH "unknown")
    configure_file(
        "${OpenAL_SOURCE_DIR}/version.h.in"
        "${OpenAL_BINARY_DIR}/version.h")
endif()


option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" ON)
if(ALSOFT_EMBED_HRTF_DATA)
    macro(make_hrtf_header FILENAME VARNAME)
        set(infile  "${OpenAL_SOURCE_DIR}/hrtf/${FILENAME}")
        set(outfile  "${OpenAL_BINARY_DIR}/${VARNAME}.txt")

        add_custom_command(OUTPUT "${outfile}"
            COMMAND ${CMAKE_COMMAND} -D "INPUT_FILE=${infile}" -D "OUTPUT_FILE=${outfile}"
                -P "${CMAKE_MODULE_PATH}/bin2h.script.cmake"
            WORKING_DIRECTORY "${OpenAL_SOURCE_DIR}"
            DEPENDS "${infile}" "${CMAKE_MODULE_PATH}/bin2h.script.cmake"
            VERBATIM
        )
        set(ALC_OBJS  ${ALC_OBJS} "${outfile}")
    endmacro()

    make_hrtf_header("Default HRTF.mhr" "default_hrtf")
endif()

# Set a 16KB page size for Android
if(ANDROID)
    set(CPP_DEFS ${CPP_DEFS} __BIONIC_NO_PAGE_SIZE_MACRO)
    check_linker_flag(C "-Wl,-z,max-page-size=16384" HAS_MAX_PAGE_SIZE_16384)
    if(HAS_MAX_PAGE_SIZE_16384)
        set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,-z,max-page-size=16384")
    endif()
endif()


if(ALSOFT_UTILS)
    find_package(MySOFA)
    if(NOT ALSOFT_NO_CONFIG_UTIL)
        find_package(Qt5Widgets QUIET)
        if(NOT Qt5Widgets_FOUND)
            message(STATUS "Could NOT find Qt5Widgets")
        endif()
    endif()
endif()
if(ALSOFT_UTILS OR ALSOFT_EXAMPLES)
    find_package(SndFile)
endif()
if(ALSOFT_EXAMPLES AND SDL3_FOUND)
    find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE)
endif()

if(NOT WIN32)
    set(LIBNAME "openal")
else()
    set(LIBNAME "OpenAL32")
endif()

# Needed for openal.pc.in
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix "\${prefix}")
set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
set(bindir "${CMAKE_INSTALL_FULL_BINDIR}")
set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
set(PACKAGE_VERSION "${LIB_VERSION}")
set(PKG_CONFIG_CFLAGS )
set(PKG_CONFIG_PRIVATE_LIBS )
if(LIBTYPE STREQUAL "STATIC")
    set(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC)
    foreach(FLAG  ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
        # If this is already a linker flag, or is a full path+file, add it
        # as-is. If it's an SDL2 or SDL3 target, add the link flag for it.
        # Otherwise, it's a name intended to be dressed as -lname.
        if(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}")
            set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}")
        elseif(FLAG MATCHES "^SDL2::SDL2")
            set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL2")
        elseif(FLAG MATCHES "^SDL3::SDL3")
            set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL3")
        else()
            set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}")
        endif()
    endforeach()
endif()

# End configuration
configure_file(
    "${OpenAL_SOURCE_DIR}/config.h.in"
    "${OpenAL_BINARY_DIR}/config.h")
configure_file(
    "${OpenAL_SOURCE_DIR}/config_backends.h.in"
    "${OpenAL_BINARY_DIR}/config_backends.h")
configure_file(
    "${OpenAL_SOURCE_DIR}/config_simd.h.in"
    "${OpenAL_BINARY_DIR}/config_simd.h")
configure_file(
    "${OpenAL_SOURCE_DIR}/openal.pc.in"
    "${OpenAL_BINARY_DIR}/openal.pc"
    @ONLY)


add_library(alsoft.common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
target_include_directories(alsoft.common PRIVATE ${OpenAL_SOURCE_DIR}/include
    PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
target_compile_definitions(alsoft.common PRIVATE ${CPP_DEFS})
target_compile_options(alsoft.common PRIVATE ${C_FLAGS})
target_link_libraries(alsoft.common PRIVATE alsoft::fmt)
set_target_properties(alsoft.common PROPERTIES ${ALSOFT_STD_VERSION_PROPS}
    POSITION_INDEPENDENT_CODE TRUE)


unset(HAS_ROUTER)
set(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal.


set(NEED_ANALYZE_SOURCE_FILES "")
foreach(obj ${CORE_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${COMMON_OBJS})
    IF (NOT ${obj} MATCHES "${CMAKE_BINARY_DIR}/default_hrtf.txt")
        list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${obj}")
    endif()
endforeach()
IF (ALSOFT_UTILS)
    list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_SOURCE_DIR}/utils/openal-info.c")
endif()
SET(CLANG_TIDY_EXECUTABLE "clang-tidy")
if(DEFINED ENV{CLANG_TIDY_EXECUTABLE})
    SET(CLANG_TIDY_EXECUTABLE $ENV{CLANG_TIDY_EXECUTABLE})
endif()
add_custom_target(clang-tidy-check ${CLANG_TIDY_EXECUTABLE} -format-style=file -p ${CMAKE_BINARY_DIR}/compile_commands.json ${NEED_ANALYZE_SOURCE_FILES} DEPENDS ${NEED_ANALYZE_SOURCE_FILES})

# Build main library
if(LIBTYPE STREQUAL "STATIC")
    add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS})
    target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC)
    target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}
        $<BUILD_LOCAL_INTERFACE:alsoft::fmt>)

    if(WIN32)
        # This option is for static linking OpenAL Soft into another project
        # that already defines the IDs. It is up to that project to ensure all
        # required IDs are defined.
        option(ALSOFT_NO_UID_DEFS "Do not define GUIDs, IIDs, CLSIDs, or PropertyKeys" OFF)
        if(ALSOFT_NO_UID_DEFS)
            target_compile_definitions(${IMPL_TARGET} PRIVATE AL_NO_UID_DEFS)
        endif()
    endif()
else()
    set(RC_CONFIG resources/openal32.rc)
    if(WIN32 AND ALSOFT_BUILD_ROUTER)
        add_library(OpenAL SHARED
            resources/router.rc
            router/router.cpp
            router/router.h
            router/alc.cpp
            router/al.cpp
        )
        target_compile_definitions(OpenAL
            PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
            "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
        target_compile_options(OpenAL PRIVATE ${C_FLAGS})
        target_link_libraries(OpenAL PRIVATE alsoft.common ${LINKER_FLAGS} alsoft::fmt)
        target_include_directories(OpenAL
          PUBLIC
            $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
          PRIVATE
            ${OpenAL_SOURCE_DIR}/common
            ${OpenAL_BINARY_DIR}
        )
        set_target_properties(OpenAL PROPERTIES ${ALSOFT_STD_VERSION_PROPS} PREFIX ""
            OUTPUT_NAME ${LIBNAME})
        if(TARGET alsoft.build_version)
            add_dependencies(OpenAL alsoft.build_version)
        endif()
        set(HAS_ROUTER 1)

        set(LIBNAME "soft_oal")
        set(IMPL_TARGET soft_oal)
        set(RC_CONFIG resources/soft_oal.rc)
    endif()

    # !important: for osx framework public header works, the headers must be in
    # the project
    set(TARGET_PUBLIC_HEADERS include/AL/al.h include/AL/alc.h include/AL/alext.h include/AL/efx.h
        include/AL/efx-presets.h)

    add_library(${IMPL_TARGET} SHARED ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS} ${RC_CONFIG}
        ${TARGET_PUBLIC_HEADERS})
    if(WIN32)
        set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "")
    endif()
    target_link_libraries(${IMPL_TARGET} PRIVATE alsoft.common ${LINKER_FLAGS} ${EXTRA_LIBS}
        ${MATH_LIB} alsoft::fmt)

    if(ALSOFT_UWP)
        find_package(cppwinrt CONFIG)
        if (TARGET Microsoft::CppWinRT)
            target_link_libraries(${IMPL_TARGET} PRIVATE Microsoft::CppWinRT)
        else()
            set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")

            find_program(NUGET_EXE NAMES nuget)
            if(NOT NUGET_EXE)
                message("NUGET.EXE not found.")
                message(FATAL_ERROR "Please install this executable, and run CMake again.")
            endif()

            exec_program(${NUGET_EXE}
                ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")

            set_target_properties(${IMPL_TARGET} PROPERTIES
                VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
            )
            target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
        endif()
    endif()

    if(NOT WIN32 AND NOT APPLE)
        # FIXME: This doesn't put a dependency on the version script. Changing
        # the version script will not cause a relink as it should.
        target_link_options(${IMPL_TARGET} PRIVATE
            "-Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
    endif()

    if(APPLE AND ALSOFT_OSX_FRAMEWORK)
        # Sets framework name to soft_oal to avoid ambiguity with the system OpenAL.framework
        set(LIBNAME "soft_oal")
        if(GIT_FOUND)
            execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD
                TIMEOUT 5
                OUTPUT_VARIABLE BUNDLE_VERSION
                OUTPUT_STRIP_TRAILING_WHITESPACE)
        else()
            set(BUNDLE_VERSION 1)
        endif()

        # Build: Fix rpath in iOS shared libraries
        # If this flag is not set, the final dylib binary ld path will be
        # /User/xxx/*/soft_oal.framework/soft_oal, which can't be loaded by iOS devices.
        # See also: https://github.com/libjpeg-turbo/libjpeg-turbo/commit/c80ddef7a4ce21ace9e3ca0fd190d320cc8cdaeb
        if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG)
            set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
        endif()

        set_target_properties(${IMPL_TARGET} PROPERTIES
            FRAMEWORK TRUE
            FRAMEWORK_VERSION C
            MACOSX_FRAMEWORK_NAME "${IMPL_TARGET}"
            MACOSX_FRAMEWORK_IDENTIFIER "org.openal-soft.openal"
            MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${LIB_VERSION}"
            MACOSX_FRAMEWORK_BUNDLE_VERSION "${BUNDLE_VERSION}"
            XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
            XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
            XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO"
            PUBLIC_HEADER "${TARGET_PUBLIC_HEADERS}"
            MACOSX_RPATH TRUE)
    endif()
endif()

target_include_directories(${IMPL_TARGET}
  PUBLIC
    $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
  INTERFACE
    $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include/AL>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/AL>
  PRIVATE
    ${INC_PATHS}
    ${OpenAL_BINARY_DIR}
    ${OpenAL_SOURCE_DIR}
    ${OpenAL_SOURCE_DIR}/common
)

set_target_properties(${IMPL_TARGET} PROPERTIES ${ALSOFT_STD_VERSION_PROPS}
    OUTPUT_NAME ${LIBNAME}
    VERSION ${LIB_VERSION}
    SOVERSION ${LIB_MAJOR_VERSION}
)
target_compile_definitions(${IMPL_TARGET}
    PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES  "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
    ${CPP_DEFS})
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})

if(TARGET alsoft.build_version)
    add_dependencies(${IMPL_TARGET} alsoft.build_version)
endif()

if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC")
    find_program(SED_EXECUTABLE NAMES sed DOC "sed executable")
    if(NOT SED_EXECUTABLE OR NOT CMAKE_DLLTOOL)
        message(STATUS "")
        if(NOT SED_EXECUTABLE)
            message(STATUS "WARNING: Cannot find sed, disabling .def/.lib generation")
        endif()
        if(NOT CMAKE_DLLTOOL)
            message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
        endif()
    else()
        target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/OpenAL32.def")
        add_custom_command(TARGET OpenAL POST_BUILD
            COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
            COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
            # Technically OpenAL32.def was created by the build, but cmake
            # doesn't recognize it due to -Wl,--output-def,OpenAL32.def being
            # manually specified. But declaring the file here allows it to be
            # properly cleaned, e.g. during make clean.
            BYPRODUCTS OpenAL32.def OpenAL32.lib
            COMMENT "Stripping ordinals from OpenAL32.def and generating OpenAL32.lib..."
            VERBATIM
        )
    endif()
endif()

if(HAS_ROUTER)
    message(STATUS "")
    message(STATUS "Building DLL router")
endif()

message(STATUS "")
message(STATUS "Building OpenAL with support for the following backends:")
message(STATUS "   ${BACKENDS}")
message(STATUS "")
message(STATUS "Building with support for CPU extensions:")
message(STATUS "    ${CPU_EXTS}")
message(STATUS "")
if(FPMATH_SET)
    message(STATUS "Building with SSE${FPMATH_SET} codegen")
    message(STATUS "")
endif()
if(ALSOFT_UWP)
    message(STATUS "Building with UWP support")
    message(STATUS "")
endif()
if(ALSOFT_EAX)
    message(STATUS "Building with legacy EAX extension support")
    message(STATUS "")
endif()

if(ALSOFT_EMBED_HRTF_DATA)
    message(STATUS "Embedding HRTF datasets")
    message(STATUS "")
endif()

# An alias for sub-project builds
add_library(OpenAL::OpenAL ALIAS OpenAL)

# Install main library
if(ALSOFT_INSTALL)
    configure_package_config_file(OpenALConfig.cmake.in OpenALConfig.cmake
        INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL)
    install(TARGETS OpenAL EXPORT OpenAL
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL)
    export(TARGETS OpenAL
        NAMESPACE OpenAL::
        FILE OpenALTargets.cmake)
    install(EXPORT OpenAL
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL
        NAMESPACE OpenAL::
        FILE OpenALTargets.cmake)
    install(DIRECTORY include/AL
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
    install(FILES "${OpenAL_BINARY_DIR}/openal.pc"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
    install(FILES "${OpenAL_BINARY_DIR}/OpenALConfig.cmake"
        DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL")
    if(TARGET soft_oal)
        install(TARGETS soft_oal
            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
    endif()
    message(STATUS "Installing library and headers")
else()
    message(STATUS "NOT installing library and headers")
endif()

if(ALSOFT_INSTALL_CONFIG)
    install(FILES alsoftrc.sample
        DESTINATION ${CMAKE_INSTALL_DATADIR}/openal)
    message(STATUS "Installing sample configuration")
endif()

if(ALSOFT_INSTALL_HRTF_DATA)
    install(DIRECTORY hrtf
        DESTINATION ${CMAKE_INSTALL_DATADIR}/openal)
    message(STATUS "Installing HRTF data files")
endif()

if(ALSOFT_INSTALL_AMBDEC_PRESETS)
    install(DIRECTORY presets
        DESTINATION ${CMAKE_INSTALL_DATADIR}/openal)
    message(STATUS "Installing AmbDec presets")
endif()
message(STATUS "")

set(UNICODE_FLAG )
if(MINGW)
    set(UNICODE_FLAG ${UNICODE_FLAG} -municode)
endif()
set(EXTRA_INSTALLS )
if(ALSOFT_UTILS)
    add_executable(openal-info utils/openal-info.c)
    target_include_directories(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common)
    target_compile_options(openal-info PRIVATE ${C_FLAGS})
    target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL ${UNICODE_FLAG})
    set_target_properties(openal-info PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
    if(ALSOFT_INSTALL_EXAMPLES)
        set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info)
    endif()

    if(SNDFILE_FOUND)
        add_executable(uhjdecoder utils/uhjdecoder.cpp)
        target_compile_definitions(uhjdecoder PRIVATE ${CPP_DEFS})
        target_include_directories(uhjdecoder
            PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
        target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
        target_link_libraries(uhjdecoder PUBLIC alsoft.common
            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt)
        set_target_properties(uhjdecoder PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(uhjencoder utils/uhjencoder.cpp)
        target_compile_definitions(uhjencoder PRIVATE ${CPP_DEFS})
        target_include_directories(uhjencoder
            PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
        target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
        target_link_libraries(uhjencoder PUBLIC alsoft.common
            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} alsoft::fmt)
        set_target_properties(uhjencoder PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
    endif()

    if(MYSOFA_FOUND)
        set(SOFA_SUPPORT_SRCS
            utils/sofa-support.cpp
            utils/sofa-support.h)
        add_library(alsoft.sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS})
        target_compile_definitions(alsoft.sofa-support PRIVATE ${CPP_DEFS})
        target_include_directories(alsoft.sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
        target_compile_options(alsoft.sofa-support PRIVATE ${C_FLAGS})
        target_link_libraries(alsoft.sofa-support PUBLIC alsoft.common MySOFA::MySOFA
            PRIVATE ${LINKER_FLAGS} alsoft::fmt)
        set_target_properties(alsoft.sofa-support PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        set(MAKEMHR_SRCS
            utils/makemhr/loaddef.cpp
            utils/makemhr/loaddef.h
            utils/makemhr/loadsofa.cpp
            utils/makemhr/loadsofa.h
            utils/makemhr/makemhr.cpp
            utils/makemhr/makemhr.h)
        add_executable(makemhr ${MAKEMHR_SRCS})
        target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
        target_include_directories(makemhr
            PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils)
        target_compile_options(makemhr PRIVATE ${C_FLAGS})
        target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} alsoft.sofa-support ${UNICODE_FLAG}
            alsoft::fmt)
        set_target_properties(makemhr PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
        if(ALSOFT_INSTALL_EXAMPLES)
            set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr)
        endif()

        set(SOFAINFO_SRCS  utils/sofa-info.cpp)
        add_executable(sofa-info ${SOFAINFO_SRCS})
        target_compile_definitions(sofa-info PRIVATE ${CPP_DEFS})
        target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils)
        target_compile_options(sofa-info PRIVATE ${C_FLAGS})
        target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} alsoft.sofa-support
            ${UNICODE_FLAG} alsoft::fmt)
        set_target_properties(sofa-info PROPERTIES ${ALSOFT_STD_VERSION_PROPS})
    endif()
    message(STATUS "Building utility programs")

    if(NOT ALSOFT_NO_CONFIG_UTIL)
        add_subdirectory(utils/alsoft-config)
    endif()
    message(STATUS "")
endif()


# Add a static library with common functions used by multiple example targets
add_library(alsoft.excommon STATIC EXCLUDE_FROM_ALL
    examples/common/alhelpers.c
    examples/common/alhelpers.h)
target_compile_definitions(alsoft.excommon PUBLIC ${CPP_DEFS})
target_include_directories(alsoft.excommon PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
target_compile_options(alsoft.excommon PUBLIC ${C_FLAGS})
target_link_libraries(alsoft.excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
set_target_properties(alsoft.excommon PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

if(ALSOFT_EXAMPLES)
    add_executable(altonegen examples/altonegen.c)
    target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} alsoft.excommon
        ${UNICODE_FLAG})
    set_target_properties(altonegen PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

    add_executable(alrecord examples/alrecord.c)
    target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG})
    set_target_properties(alrecord PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

    add_executable(aldebug examples/aldebug.cpp)
    target_link_libraries(aldebug PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG}
        alsoft::fmt)
    set_target_properties(aldebug PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

    add_executable(allafplay examples/allafplay.cpp)
    target_link_libraries(allafplay PRIVATE ${LINKER_FLAGS} alsoft.common alsoft.excommon
        ${UNICODE_FLAG} alsoft::fmt)
    set_target_properties(allafplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

    if(ALSOFT_INSTALL_EXAMPLES)
        set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord aldebug allafplay)
    endif()

    message(STATUS "Building example programs")

    if(SNDFILE_FOUND)
        add_executable(alplay examples/alplay.c)
        target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
            ${UNICODE_FLAG})
        set_target_properties(alplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(alstream examples/alstream.c)
        target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
            ${UNICODE_FLAG})
        set_target_properties(alstream PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(alreverb examples/alreverb.c)
        target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
            ${UNICODE_FLAG})
        set_target_properties(alreverb PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(almultireverb examples/almultireverb.c)
        target_link_libraries(almultireverb
            PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG})
        set_target_properties(almultireverb PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(allatency examples/allatency.c)
        target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
            ${UNICODE_FLAG})
        set_target_properties(allatency PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(alhrtf examples/alhrtf.c)
        target_link_libraries(alhrtf
            PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG})
        set_target_properties(alhrtf PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(alstreamcb examples/alstreamcb.cpp)
        target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
            ${UNICODE_FLAG} alsoft::fmt)
        set_target_properties(alstreamcb PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(aldirect examples/aldirect.cpp)
        target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon
            ${UNICODE_FLAG} alsoft::fmt)
        set_target_properties(aldirect PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        add_executable(alconvolve examples/alconvolve.c)
        target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alsoft.common SndFile::SndFile
            alsoft.excommon ${UNICODE_FLAG})
        set_target_properties(alconvolve PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        if(ALSOFT_INSTALL_EXAMPLES)
            set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
                alhrtf aldirect)
        endif()

        message(STATUS "Building SndFile example programs")
    endif()

    # Can't safely use SDL3 and SDL2 together
    if(SDL3_FOUND AND NOT HAVE_SDL2)
        message(STATUS "Building SDL3 example programs")

        add_executable(alloopback examples/alloopback.c)
        target_link_libraries(alloopback
            PRIVATE ${LINKER_FLAGS} SDL3::SDL3 alsoft.excommon ${MATH_LIB})
        set_target_properties(alloopback PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

        if(ALSOFT_INSTALL_EXAMPLES)
            set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback)
        endif()

        set(FFVER_OK FALSE)
        if(FFMPEG_FOUND)
            set(FFVER_OK TRUE)
            if(AVFORMAT_VERSION VERSION_LESS "59.27.100")
                message(STATUS "libavformat is too old! (${AVFORMAT_VERSION}, wanted 59.27.100)")
                set(FFVER_OK FALSE)
            endif()
            if(AVCODEC_VERSION VERSION_LESS "59.37.100")
                message(STATUS "libavcodec is too old! (${AVCODEC_VERSION}, wanted 59.37.100)")
                set(FFVER_OK FALSE)
            endif()
            if(AVUTIL_VERSION VERSION_LESS "57.28.100")
                message(STATUS "libavutil is too old! (${AVUTIL_VERSION}, wanted 57.28.100)")
                set(FFVER_OK FALSE)
            endif()
            if(SWSCALE_VERSION VERSION_LESS "6.7.100")
                message(STATUS "libswscale is too old! (${SWSCALE_VERSION}, wanted 6.7.100)")
                set(FFVER_OK FALSE)
            endif()
            if(SWRESAMPLE_VERSION VERSION_LESS "4.7.100")
                message(STATUS "libswresample is too old! (${SWRESAMPLE_VERSION}, wanted 4.7.100)")
                set(FFVER_OK FALSE)
            endif()
        endif()
        if(FFVER_OK)
            add_executable(alffplay examples/alffplay.cpp)
            target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS})
            target_link_libraries(alffplay
                PRIVATE ${LINKER_FLAGS} SDL3::SDL3 ${FFMPEG_LIBRARIES} alsoft.excommon alsoft::fmt)
            set_target_properties(alffplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS})

            if(ALSOFT_INSTALL_EXAMPLES)
                set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay)
            endif()
            message(STATUS "Building SDL3+FFmpeg example programs")
        endif()
    endif()
    message(STATUS "")
endif()

if(ALSOFT_TESTS)
add_subdirectory(tests)
endif()

if(EXTRA_INSTALLS)
    install(TARGETS ${EXTRA_INSTALLS}
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        BUNDLE  DESTINATION ${CMAKE_INSTALL_BINDIR}
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
